package com.XDApp.xdbase.dao.base;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import com.XDApp.xdbase.dao.DBHelper;
import com.XDApp.xdbase.dao.annotation.Column;
import com.XDApp.xdbase.dao.annotation.ID;
import com.XDApp.xdbase.dao.annotation.TableName;

/**
 * 实现类的公共部分
 * 
 * @author Administrator
 * 
 * @param <M>
 */
public abstract class DAOSupport<M> implements DAO<M> {

	// 问题一：表名获取
	// 问题二：将实体中字段数据设置给数据表中对应的列
	// 问题三：设置数据到实体的对应字段中
	// 问题四：获取到实体中主键的数据
	// 问题五：实体对应的实例创建

	private static final String TAG = "DAOSupport";
	protected Context context;
	protected DBHelper helper;
	protected SQLiteDatabase database;

	public DAOSupport(Context context) {
		super();
		this.context = context;
		helper = new DBHelper(context);
		database = helper.getWritableDatabase();
	}

	@Override
	public long insert(M m) {
		ContentValues values = new ContentValues();

		// values.put(DBHelper.TABLE_NEWS_TITLE, news.getTitle());
		// 将所有的含有@Column的字段，将数据设置给ContentValues.put(@Column.value(),field.get())
		fillContentValues(m, values);// 第一个参数：数据源；第二参数：设置的目标对象

		return database.insert(getTableName(), null, values);
	}

	@Override
	public int delete(Serializable id) {
		return database.delete(getTableName(), DBHelper.TABLE_ID + "=?", new String[] { id.toString() });
	}

	@Override
	public int update(M m) {
		ContentValues values = new ContentValues();

		fillContentValues(m, values);

		return database.update(getTableName(), values, DBHelper.TABLE_ID + "=?", new String[] { getId(m) });
	}

	public List<M> findByCondition(String selection, String[] selectionArgs, String orderBy) {
		return findByCondition(null, selection, selectionArgs, null, null, orderBy);
	}

	/**
	 * 按照条件查询
	 * 
	 * @param columns
	 * @param selection
	 * @param selectionArgs
	 * @param groupBy
	 * @param having
	 * @param orderBy
	 * @return
	 */
	public List<M> findByCondition(String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy) {
		List<M> result = null;

		Cursor cursor = null;
		try {
			cursor = database.query(getTableName(), columns, selection, selectionArgs, groupBy, having, orderBy);
			if (cursor != null) {
				result = new ArrayList<M>();// List<M> result=new ArrayList<M>();
				while (cursor.moveToNext()) {
					// News item = new News();// M m=new M();
					M m = getInstance();

					// 设置数据
					fillField(cursor, m);// 数据源，设置的目标
					result.add(m);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (cursor != null)
				cursor.close();
		}

		return result;
	}

	@Override
	public List<M> findAll() {
		return findByCondition(null, null, null);
	}

	/**
	 * 问题一：表名获取
	 * 
	 * @return
	 */
	private String getTableName() {
		// 方案一：实体，获取实体的简单名称，News news(表名)
		// 方案二：注解,在News类上添加一个注解信息（@TableName(DBHelper.TABLE_NEWS_NAME)），运行时获取注解信息
		M m = getInstance();
		TableName tableName = m.getClass().getAnnotation(TableName.class);
		if (tableName != null) {
			return tableName.value();
		}

		return null;
	}

	/**
	 * 问题二：将实体中字段数据设置给数据表中对应的列
	 * 
	 * @param m
	 * @param values
	 */
	private void fillContentValues(M m, ContentValues values) {
		// values.put(DBHelper.TABLE_NEWS_TITLE, news.getTitle());
		// 将所有的含有@Column的字段，将数据设置给ContentValues.put(@Column.value(),field.get())
		// objects for all public fields
		// m.getClass().getFields();

		Field[] fields = m.getClass().getDeclaredFields();// private
		for (Field item : fields) {
			item.setAccessible(true);// 暴力反射

			Column column = item.getAnnotation(Column.class);
			if (column != null) {
				String key = column.value();
				try {
					// Returns the value of the field in the specified object.
					String value = item.get(m).toString();

					ID id = item.getAnnotation(ID.class);
					if (id != null) {
						boolean autoincrement = id.autoincrement();
						if (autoincrement) {
							// 主键+自增 不能设置值
						} else {
							values.put(key, value);// sqlite 字符串（主键+自增）
						}
					} else {
						values.put(key, value);// sqlite 字符串（主键+自增）

					}

				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		}

	}

	/**
	 * 问题三：设置数据到实体的对应字段中
	 * 
	 * @param m
	 * @param cursor
	 */
	private void fillField(Cursor cursor, M m) {
		// int columnIndex = cursor.getColumnIndex(DBHelper.TABLE_NEWS_TITLE);
		// String title = cursor.getString(columnIndex);
		// item.setTitle(title);

		// 此处省略n行代码

		Field[] fields = m.getClass().getDeclaredFields();

		for (Field item : fields) {
			item.setAccessible(true);
			Column column = item.getAnnotation(Column.class);
			if (column != null) {
				int columnIndex = cursor.getColumnIndex(column.value());
				String value = cursor.getString(columnIndex);
				// Sets the value of the field in the specified object to the value.

				try {
					// TODO 设置时需要判断(int long)
					if (item.getType() == int.class) {
						item.set(m, Integer.parseInt(value));
					} else if (item.getType() == long.class) {

						item.set(m, Long.parseLong(value));
					} else {
						item.set(m, value);
					}
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}

			}

		}

	}

	/**
	 * 问题四：获取到实体中主键的数据
	 * 
	 * @param m
	 * @return
	 */
	private String getId(M m) {
		Field[] fields = m.getClass().getDeclaredFields();
		for (Field item : fields) {
			item.setAccessible(true);
			ID id = item.getAnnotation(ID.class);
			if (id != null) {
				try {
					return item.get(m).toString();
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}

	/**
	 * 问题五：实体对应的实例创建
	 * 
	 * @return
	 */
	private M getInstance() {
		// 获取到Class,newInstance();
		// 实体对应的class，在哪里出现
		// ①获取到NewsDaoImpl,BookDaoImpl......，运行时那个子类
		// ②获取NewsDaoImpl的父类（X），获取支持泛型的父类
		// ③获取泛型中世纪传输的参数
		// ④newInstance();

		// 返回此 Object 的运行时类。
		Class clazz = getClass();
		// Log.i(TAG, clazz.getName());

		// clazz.getSuperclass();//cn.ithm.dbhm23.dao.impl.NewsDaoImpl
		Type genericSuperclass = clazz.getGenericSuperclass();// cn.ithm.dbhm23.dao.impl.NewsDaoImpl<cn.ithm.dbhm23.dao.domain.News>
		// 参数化的类型：所有的泛型都会实现的 接口
		if (genericSuperclass instanceof ParameterizedType) {
			Type type = ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0];// cn.ithm.dbhm23.dao.domain.News
			Class targetClazz = (Class) type;
			try {
				return (M) targetClazz.newInstance();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}

		return null;
	}
}
